//
//  CLBook.m
//  CLBrowser
//
//  Created by 梁慧聪 on 2022/2/9.
//

#import "CLBook.h"
#import "CLKeyedArchiver.h"
#import <objc/runtime.h>
#import <YYKit/YYClassInfo.h>
@interface CLReadChapterModel ()
@property (atomic, assign) BOOL isHadAddObserver;
@property (nonatomic, copy) void(^completeBlock)(void);

// MARK: -- 更新字体
@property (nonatomic, strong) NSDictionary *attributes;

@end

@implementation CLReadChapterModel
@synthesize name = _name;
- (NSDictionary *)attributes {
    if (!_attributes) {
        _attributes = [NSDictionary dictionary];
    }
    return _attributes;
}

+ (NSString *)getTableName { return @"CLReadChapterModelTable"; }

+ (NSString *)getPrimaryKey { return @"chapterid"; }

+ (int)getTableVersion { return 1; }

- (void)requestWithCompleteBlock:(void(^)(void))completeBlock {
    if (NULLString(self.chapterid) || self.isHadAddObserver) { return; }
    WEAK_SELF;
    self.isHadAddObserver = YES;
    self.completeBlock = completeBlock;
   
    [self pp_addObserverForKeyPath:@"content" options:NSKeyValueObservingOptionNew changeAction:^(id  _Nullable object, NSDictionary<NSKeyValueChangeKey,id> * _Nullable change, NSString * _Nullable keyPath) {
        JSLog(@"[native]收到章节内容:<%p>%@,%ld",weakSelf,weakSelf.chapterid,weakSelf.content.length);
        CLSafeExecuteBlock(weakSelf.completeBlock);
    }];
}

- (void)removeObserverAndBlock {
    self.completeBlock = nil;
}

- (void)dealloc {
    if (!self.isTemp) JSLog(@"CLReadChapterModel <%p>%@ 释放了",self,self.chapterid);
    [Notifier removeObserver:self];
}

- (NSString *)chapterid {
    if (_chapterid) {
        return _chapterid;
    }
    if (!NULLString(self.domain) && !NULLString(self.author) && !NULLString(self.title) &&  !NULLString(self.name)) {
        NSString *chapterid = [NSString stringWithFormat:[CLReadChapterModel chapteridFormat],self.domain,self.title,self.author,self.name];
        if (![chapterid isEqualToString:_chapterid]) {
//            NSAssert3(0, @"章节出错出错:%p,%@ != %@",self,_chapterid,chapterid);
            JSLog(@"章节出错出错:%p,%@ != %@",self,_chapterid,chapterid);
            return nil;
        }
        return chapterid;
    }
    return nil;
}

+ (NSString *)chapteridFormat { return @"<%@>%@(%@)-%@"; }

- (BOOL)isValid { return !NULLString(self.chapterid); }

- (BOOL)isFirstChapter { return (self.previousChapterID == CL_READ_NO_MORE_CHAPTER); }

- (BOOL)isLastChapter { return self.nextChapterID == CL_READ_NO_MORE_CHAPTER; }

- (NSString *)fullName { return CL_READ_CHAPTER_NAME(self.name); }

- (CGFloat)pageTotalHeight {
    CGFloat pageTotalHeight = 0;
    for (CLReadPageModel *pageModel in self.pageModels) {
        pageTotalHeight += (pageModel.contentSize.height + pageModel.headTypeHeight);
    }
    return pageTotalHeight;
}

/// 更新字体
- (void)updateFont {
    NSDictionary *tempAttributes = [CLReadConfigure.sharedInstance attributesByIsTitle:NO isPageing:YES];
    __block BOOL isEqualTo = YES;
    if (tempAttributes.count != self.attributes.count) {
        isEqualTo = NO;
    } else {
        [tempAttributes enumerateKeysAndObjectsUsingBlock:^(NSString  * _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            id oObj = [self.attributes objectForKey:key];
            if (oObj) {
                if ([key isEqualToString:@"NSFont"]) {
                    UIFont *font = obj;
                    UIFont *oFont = oObj;
                    if (![font.familyName isEqualToString:oFont.familyName] || font.pointSize != oFont.pointSize) {
                        isEqualTo = NO;
                        *stop = YES;
                    }
                } else if ([key isEqualToString:@"NSParagraphStyle"]) {
                    NSParagraphStyle *style = obj;
                    NSParagraphStyle *oStyle = oObj;
                    
                    if (style.lineSpacing != oStyle.lineSpacing ||
                        style.paragraphSpacing != oStyle.paragraphSpacing ||
                        style.alignment != oStyle.alignment ||
                        style.headIndent != oStyle.headIndent ||
                        style.tailIndent != oStyle.tailIndent ||
                        style.firstLineHeadIndent != oStyle.firstLineHeadIndent ||
                        style.minimumLineHeight != oStyle.minimumLineHeight ||
                        style.maximumLineHeight != oStyle.maximumLineHeight ||
                        style.lineBreakMode != oStyle.lineBreakMode ||
                        style.baseWritingDirection != oStyle.baseWritingDirection ||
                        style.lineHeightMultiple != oStyle.lineHeightMultiple ||
                        style.paragraphSpacingBefore != oStyle.paragraphSpacingBefore ||
                        style.hyphenationFactor != oStyle.hyphenationFactor ||
                        style.defaultTabInterval != oStyle.defaultTabInterval ||
                        style.allowsDefaultTighteningForTruncation != oStyle.allowsDefaultTighteningForTruncation ||
                        style.lineBreakStrategy != oStyle.lineBreakStrategy) {
                        isEqualTo = NO;
                        *stop = YES;
                    }
                }
            } else {
                isEqualTo = NO;
                *stop = YES;
            }
        }];
    }
    
    
    if (!isEqualTo) {
        self.attributes = tempAttributes;
        self.fullContent = [self fullContentAttrString];
        self.pageModels = [CLReadParser pageingWithAttrString:self.fullContent rect:CGRectMake(0, 0, CL_READ_VIEW_RECT().size.width, CL_READ_VIEW_RECT().size.height) isFirstChapter:self.isFirstChapter];
        self.pageCount = self.pageModels.count;
        [self save];
    }
}

/// 完整内容排版
- (NSMutableAttributedString *)fullContentAttrString {
    NSMutableAttributedString *titleString = [[NSMutableAttributedString alloc]initWithString:self.fullName attributes:[CLReadConfigure.sharedInstance attributesByIsTitle:YES isPageing:NO]];
    NSMutableAttributedString *contentString = [[NSMutableAttributedString alloc]initWithString:self.content attributes:[CLReadConfigure.sharedInstance attributesByIsTitle:NO isPageing:NO]];
    [titleString appendAttributedString:contentString];
    return titleString;
}
/// 保存
- (void)save {
    NSLog(@"保存 bookid:%@-%@",self.bookid,self.chapterid);
    [CLKeyedArchiver archiver:self.bookid fileName:self.chapterid object:self];
    [self saveToDB];
}

// MARK: 辅助功能

/// 获取指定页码字符串
- (NSString *)contentString:(NSInteger)page {
    return self.pageModels[page].content.string;
}

/// 获取指定页码富文本
- (NSAttributedString *)contentAttributedString:(NSInteger)page {
    return self.pageModels[page].showContent;
}

/// 获取指定页开始坐标
- (NSUInteger)locationFirst:(NSInteger)page {
    return self.pageModels[page].range.location;
}

/// 获取指定页码末尾坐标
- (NSUInteger)locationLast:(NSInteger)page {
    NSRange range = self.pageModels[page].range;
    return range.location + range.length;
}

/// 获取指定页中间
- (NSUInteger)locationCenter:(NSInteger)page {
    NSRange range = self.pageModels[page].range;
    return (range.location + (range.location + range.length) / 2);
}

/// 获取存在指定坐标的页码
- (NSUInteger)page:(NSInteger)location {
    NSUInteger count = self.pageModels.count;
    for (int i = 0; i < count; i++) {
        NSRange range = self.pageModels[i].range;
        if (location < (range.location + range.length)) {
            return i;
        }
    }
    return 0;
}

/// 是否存在章节内容
+ (BOOL)isExist:(NSString *)bookid chapterid:(NSString *)chapterid {
    BOOL isExist = [CLKeyedArchiver isExist:bookid fileName:chapterid];
    NSLog(@"是否存在章节内容-%@ bookid:%@-%@",@(isExist),bookid,chapterid);
    return isExist;
}

// MARK: 构造

/// 获取章节对象,如果则创建对象返回
+ (CLReadChapterModel *)model:(NSString *)bookid chapterid:(NSString *)chapterid {
    CLReadChapterModel *readChapterModel = (CLReadChapterModel *)[CLReadChapterModel model:bookid chapterid:chapterid isUpdateFont:YES];
    return readChapterModel;
}
+ (CLReadChapterModel *)model:(NSString *)bookid chapterid:(NSString *)chapterid isUpdateFont:(BOOL)isUpdateFont {
    
    CLReadChapterModel *chapterModel = nil;
    if ([CLKeyedArchiver isExist:bookid fileName:chapterid]) {
        chapterModel = [CLKeyedArchiver unarchiver:bookid fileName:chapterid];
        if (isUpdateFont) { [chapterModel updateFont]; }
    } else {
        chapterModel = [[CLReadChapterModel alloc]init];
        chapterModel.bookid = bookid;
        chapterModel.chapterid = chapterid;
    }
    return chapterModel;
}

- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        _bookid = [aDecoder decodeObjectForKey:@"bookid"];
        _chapterid = [aDecoder decodeObjectForKey:@"chapterid"];
        _previousChapterID = [aDecoder decodeObjectForKey:@"previousChapterID"];
        _nextChapterID = [aDecoder decodeObjectForKey:@"nextChapterID"];
        _name = [aDecoder decodeObjectForKey:@"name"];
        _priority = [aDecoder decodeInt64ForKey:@"priority"];
        _content = [aDecoder decodeObjectForKey:@"content"];
        _fullContent = [aDecoder decodeObjectForKey:@"fullContent"];
        _pageCount = [aDecoder decodeInt64ForKey:@"pageCount"];
        _pageModels = [aDecoder decodeObjectForKey:@"pageModels"];
        _attributes = [aDecoder decodeObjectForKey:@"attributes"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    
    if (_bookid) {
        [aCoder encodeObject:_bookid forKey:@"bookid"];
    }
    
    if (_chapterid) {
        [aCoder encodeObject:_chapterid forKey:@"chapterid"];
    }
    
    if (_previousChapterID) {
        [aCoder encodeObject:_previousChapterID forKey:@"previousChapterID"];
    }
    
    if (_nextChapterID) {
        [aCoder encodeObject:_nextChapterID forKey:@"nextChapterID"];
    }
    
    if (_name) {
        [aCoder encodeObject:_name forKey:@"name"];
    }
    
    
    [aCoder encodeInt64:_priority forKey:@"priority"];
    
    
    if (_content) {
        [aCoder encodeObject:_content forKey:@"content"];
    }
    
    if (_fullContent) {
        [aCoder encodeObject:_fullContent forKey:@"fullContent"];
    }
    
    [aCoder encodeInt64:_pageCount forKey:@"pageCount"];

    if (_fullContent) {
        [aCoder encodeObject:_fullContent forKey:@"fullContent"];
    }
    
    if (_pageModels && _pageModels.count) {
        [aCoder encodeObject:_pageModels forKey:@"pageModels"];
    }
    
    if (_attributes) {
        [aCoder encodeObject:_attributes forKey:@"attributes"];
    }
    
//    unsigned int count;// 记录属性个数
//    objc_property_t *properties = class_copyPropertyList([self class], &count);
//    if (properties) {
//        for (unsigned int i = 0; i < count; i++) {
//            objc_property_t property = properties[i];
//            YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:property];
//            if (info.name) {
//                id value = [self valueForKey:info.name];//KVC
//                if ([value conformsToProtocol:@protocol(NSCoding)]) {
//                    [aCoder encodeObject:value forKey:info.name];
//                }
//            }
//        }
//        free(properties);
//    }
}

- (void)setName:(NSString *)name {
    _name = name;
    _title = name;
}

- (NSString *)name {
    return StrNilHolder(_name, _title);
}
@end

@interface CLBook ()
@property (atomic, assign) BOOL isHadAddObserver;
@property (nonatomic, copy) void(^cl_dataUpdateBlock)(void);
@end

@implementation CLBook

CLEncodeMethod;


+ (NSDictionary *)modelContainerPropertyGenericClass {
    return @{@"catalogue":[CLReadChapterModel class]};
}

+ (NSString *)getTableName {
    return @"CLBookTable";
}

+ (NSString *)getPrimaryKey {
    return @"bookid";
}
// 表版本
+ (int)getTableVersion {
    return 1;
}

- (void)addObserverWithUpdateBlock:(void(^)(void))updateBlock {
    if (NULLString(self.bookid) || self.isHadAddObserver) {
        return;
    }
    WEAK_SELF;
    self.isHadAddObserver = YES;
    self.cl_dataUpdateBlock = updateBlock;
    [self pp_addObserverForKeyPath:@"catalogue" options:NSKeyValueObservingOptionNew changeAction:^(id  _Nullable object, NSDictionary<NSKeyValueChangeKey,id> * _Nullable change, NSString * _Nullable keyPath) {
        CLSafeExecuteBlock(weakSelf.cl_dataUpdateBlock);
    }];
}

- (void)dealloc {
    if (!self.isTemp) JSLog(@"CLBook <%p>%@ 释放了",self,self.bookid);
    [Notifier removeObserver:self];
}

- (void)setIsTemp:(BOOL)isTemp {
    _isTemp = isTemp;
    for (CLReadChapterModel *chapter in self.catalogue) {
        chapter.isTemp = isTemp;
    }
}

- (BOOL)isValid { return !NULLString(self.bookid); }

- (NSString *)bookid {
    if (!NULLString(self.domain) && !NULLString(self.author) && !NULLString(self.title)) {
        NSString *bookid = [NSString stringWithFormat:[CLBook bookidFormat],self.domain,self.title,self.author];
        if (![bookid isEqualToString:_bookid]) {
            NSAssert3(0, @"书籍出错:%p,%@ != %@",self,_bookid,bookid);
            return nil;
        }
        return bookid;
    }
    return nil;
}

+ (NSString *)bookidFormat { return @"<%@>%@(%@)"; }

@end

@implementation CLReadRecordModel

- (CLReadPageModel *)pageModel { return self.chapterModel.pageModels[self.page]; }

- (NSUInteger)locationFirst { return [self.chapterModel locationFirst:self.page]; }
/// 当前记录末尾坐标
- (NSUInteger)locationLast { return [self.chapterModel locationLast:self.page]; }

/// 当前记录是否为第一个章节
- (BOOL)isFirstChapter { return self.chapterModel.isFirstChapter; }

/// 当前记录是否为最后一个章节
- (BOOL)isLastChapter { return self.chapterModel.isLastChapter; }

/// 当前记录是否为第一页
- (BOOL)isFirstPage { return (self.page == 0); }

/// 当前记录是否为最后一页
- (BOOL)isLastPage { return (self.page == (self.chapterModel.pageCount - 1)); }

/// 当前记录页码字符串
- (NSString *)contentString { return [self.chapterModel contentString:self.page]; }

- (NSAttributedString *)contentAttributedString { return [self.chapterModel contentAttributedString:self.page]; }

/// 当前记录切到上一页
- (void)previousPage { self.page = MAX(self.page - 1, 0); }

/// 当前记录切到下一页
- (void)nextPage { self.page = MIN(self.page + 1, self.chapterModel.pageCount - 1); }

/// 当前记录切到第一页
- (void)firstPage { self.page = 0; }

/// 当前记录切到最后一页
- (void)lastPage { self.page = self.chapterModel.pageCount - 1; }

// MARK: 辅助

/// 修改阅读记录为指定章节位置
- (void)modify:(CLReadChapterModel *)chapterModel page:(NSInteger)page {
    [self modify:chapterModel page:page isSave:YES];
}
- (void)modify:(CLReadChapterModel *)chapterModel page:(NSInteger)page isSave:(BOOL)isSave {
    
    self.chapterModel = chapterModel;
    
    self.page = page;
    
    if (isSave) { [self save]; }
}

/// 修改阅读记录为指定章节位置
- (void)modify:(NSString *)chapterid location:(NSInteger)location isSave:(BOOL)isSave {
    if ([CLReadChapterModel isExist:self.bookid chapterid:chapterid]) {
        self.chapterModel = [CLReadChapterModel model:self.bookid chapterid:chapterid isUpdateFont:YES];
        self.page = [self.chapterModel page:location];
        if (isSave) { [self save]; }
    }
}
/// 保存记录
- (void)save {
    NSLog(@"保存记录: %@",self.bookid);
    [CLKeyedArchiver archiver:self.bookid fileName:CL_READ_KEY_RECORD object:self];
}
/// 修改阅读记录为指定章节页码 (toPage == DZM_READ_LAST_PAGE 为当前章节最后一页)
- (void)modify:(NSString *)chapterid toPage:(NSInteger)toPage isSave:(BOOL)isSave {
    
    if ([CLReadChapterModel isExist:self.bookid chapterid:chapterid]) {
        
        self.chapterModel = [CLReadChapterModel model:self.bookid chapterid:chapterid isUpdateFont:YES];
        
        if (toPage == CL_READ_LAST_PAGE) {
            [self lastPage];
        } else { self.page = toPage; }
        
        if (isSave) { [self save]; }
    }
}

/// 更新字体
- (void)updateFont:(BOOL)isSave {
    
    if (self.chapterModel != nil) {
        
        [self.chapterModel updateFont];
        
        self.page = [self.chapterModel page:CL_READ_RECORD_CURRENT_CHAPTER_LOCATION];
        
        if (isSave) { [self save]; }
    }
}

/// 拷贝阅读记录
- (CLReadRecordModel *)copyModel {
    
    CLReadRecordModel *recordModel = [[CLReadRecordModel alloc]init];
    
    recordModel.bookid = self.bookid;
    
    recordModel.chapterModel = self.chapterModel;
    
    recordModel.page = self.page;
    
    return recordModel;
}



/// 是否存在阅读记录
+ (BOOL)isExist:(NSString *)bookid {
    BOOL isExist = [CLKeyedArchiver isExist:bookid fileName:CL_READ_KEY_RECORD];
    NSLog(@"是否存在阅读记录:%@",bookid);
    return isExist;
}


// MARK: 构造

/// 获取阅读记录对象,如果则创建对象返回
+ (CLReadRecordModel *)model:(NSString *)bookid {
    
    CLReadRecordModel *recordModel = nil;
    
    if ([CLReadRecordModel isExist:bookid]) {
        
        recordModel = [CLKeyedArchiver unarchiver:bookid fileName:CL_READ_KEY_RECORD];
        
        [recordModel.chapterModel updateFont];
        
    }else{
        
        recordModel = [CLReadRecordModel.alloc init];
        
    }
    recordModel.bookid = bookid;
    
    return recordModel;
}

- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        _bookid = [aDecoder decodeObjectForKey:@"bookid"];
        
        _chapterModel = [aDecoder decodeObjectForKey:@"chapterModel"];
        
        _page = [aDecoder decodeIntegerForKey:@"page"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    if (_bookid) {
        [aCoder encodeObject:_bookid forKey:@"bookid"];
    }
    
    if (_chapterModel) {
        [aCoder encodeObject:_chapterModel forKey:@"chapterModel"];
    }
    
    [aCoder encodeInteger:_page forKey:@"page"];
}
@end

@implementation CLReadPageModel

- (instancetype)init {
    self = [super init];
    if (self) {
        
    }
    return self;
}

// 内容高度 + 头部高度
- (CGFloat)cellHeight { return self.contentSize.height +self.headTypeHeight; }

- (BOOL)isHomePage { return self.range.location == CL_READ_BOOK_HOME_PAGE; }

- (NSAttributedString *)showContent {
    if (self.content == nil) {
        return nil;
    }
    if (NULLString(self.content.string)) {
        return nil;
    }
    UIColor *textColor = CLReadConfigure.sharedInstance.textColor;
    NSMutableAttributedString *tempShowContent = [[NSMutableAttributedString alloc] initWithAttributedString:self.content];
    [tempShowContent addAttributes:@{NSForegroundColorAttributeName:textColor} range:NSMakeRange(0, self.content.length)];
    NSLog(@"%ld,内容:%@",self.page,[tempShowContent.string substringToIndex:5]);
    return tempShowContent;
}

- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        _content = [aDecoder decodeObjectForKey:@"content"];
        
        _range = [[aDecoder decodeObjectForKey:@"range"] rangeValue];
        
        _page = [aDecoder decodeIntegerForKey:@"page"];
        
        _headTypeHeight = [aDecoder decodeFloatForKey:@"headTypeHeight"];
        
        _contentSize = [[aDecoder decodeObjectForKey:@"contentSize"] CGSizeValue];
        
        _headType = [aDecoder decodeIntegerForKey:@"headType"];
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    if (_content) {
        [aCoder encodeObject:_content forKey:@"content"];
    }
    
    [aCoder encodeObject:[NSValue valueWithRange:_range] forKey:@"range"];
    
    [aCoder encodeInteger:_page forKey:@"page"];
    
    [aCoder encodeFloat:_headTypeHeight forKey:@"headTypeHeight"];
    
    [aCoder encodeObject:[NSValue valueWithCGSize:_contentSize] forKey:@"contentSize"];
    
    [aCoder encodeInteger:_headType forKey:@"headType"];
    
}
@end


@implementation CLReadMarkModel


- (id)initWithCoder:(NSCoder *)aDecoder{
    if (self = [super initWithCoder:aDecoder]) {
        
        _bookid = [aDecoder decodeObjectForKey:@"bookid"];
        _chapterid = [aDecoder decodeObjectForKey:@"chapterid"];
        _name = [aDecoder decodeObjectForKey:@"name"];
        _content = [aDecoder decodeObjectForKey:@"content"];
        _time = [aDecoder decodeDoubleForKey:@"time"];
        _location = [aDecoder decodeInt64ForKey:@"location"];
     
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    
    if (_bookid) {
        [aCoder encodeObject:_bookid forKey:@"bookid"];
    }
    if (_chapterid) {
        [aCoder encodeObject:_chapterid forKey:@"chapterid"];
    }
    if (_name) {
        [aCoder encodeObject:_name forKey:@"name"];
    }
    if (_content) {
        [aCoder encodeObject:_content forKey:@"content"];
    }
    [aCoder encodeDouble:_time forKey:@"time"];
    [aCoder encodeInt64:_location forKey:@"location"];
}

@end
